//
//  ExpenseViewController.m
//  iExpense
//
//  Created by WU, Zhenyu on 7/1/09.
//  Copyright 2009 National Instruments. All rights reserved.
//

#import "ExpenseViewController.h"
#import "iExpenseAppDelegate.h"
#import "ExpenseEditViewController.h"
#import "Expense.h"
#import "Currency.h"
#import "Setting.h"
#import "Type.h"


@implementation ExpenseViewController

@synthesize listTableView;
@synthesize fetchedResultsController;
@synthesize managedObjectContext;
@synthesize addingManagedObjectContext;

#pragma mark -
#pragma mark Table Data Source Methods

/*
 The data source methods are handled primarily by the fetch results controller
 */

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
	NSUInteger sectionNum = [[fetchedResultsController sections] count];
	if(sectionNum > 0) {
		return sectionNum;
	} else {
		return 1;
	}
}

-(NSInteger) tableView: (UITableView*)tableView
 numberOfRowsInSection:(NSInteger) section {
	if([[fetchedResultsController sections] count] == 0) {
		return 0;
	}
 	id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
	return [sectionInfo numberOfObjects];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
	static NSString *ExpenseCellID = @"ExpenseCellID";
	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ExpenseCellID];
	if(cell == nil) {
		cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
									   reuseIdentifier:ExpenseCellID]
				autorelease];
	}

	[self configureCell:cell atIndexPath:indexPath];
	return cell;
}

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
	
    // Configure the cell to show the book's title
	Expense *expense = [fetchedResultsController objectAtIndexPath:indexPath];
	NSString *text = [[NSString alloc] initWithFormat:@"%@ %@%1.2f",
					  expense.Type.Name, expense.Currency.Symbol, [expense.Amount doubleValue]];
	NSString *detail = [[NSString alloc] initWithFormat:@"%@: %@",
						expense.Date, expense.Note];
	
	cell.textLabel.text = text;
	cell.detailTextLabel.text = detail;
	
	[text release];
	[detail release];

}
/*
 // The designated initializer.  Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        // Custom initialization
    }
    return self;
}
*/

/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/

-(void) addExpense {
	NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
	self.addingManagedObjectContext = addingContext;
	[addingContext release];
	
	[addingManagedObjectContext setPersistentStoreCoordinator:[[fetchedResultsController managedObjectContext] persistentStoreCoordinator]];
	//Get the Setting first
	
	NSFetchRequest		*requeset = [[NSFetchRequest alloc] init];
	NSEntityDescription *entity	  = [NSEntityDescription entityForName:@"Setting"
												inManagedObjectContext:self.addingManagedObjectContext];
	[requeset setEntity:entity];
	
	NSError *error;
	NSMutableArray *fetchedResults = [[self.addingManagedObjectContext executeFetchRequest: requeset
									   error:&error] mutableCopy];
	Setting *setting = nil;
	if(fetchedResults != nil && [fetchedResults count] != 0) {
		setting = [fetchedResults objectAtIndex:0];
	} else {
		NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
		exit(-1);  // Fail
	}
	[requeset release];
	
	
	// Create new object, expense
	
	Expense *expense = (Expense *)[NSEntityDescription insertNewObjectForEntityForName:@"Expense" inManagedObjectContext:addingContext];
	NSNumber *amount = [[NSNumber alloc] initWithDouble:0.0];
	expense.Amount = amount;
	[amount release];
	expense.Category = setting.Category;
	expense.Currency = setting.Currency;
	NSDate *today = [NSDate date]; // autoreleased, should not be released
	expense.Date = today;
	
	expense.Identity = setting.LastIdentity;
	NSNumber *newIdentity = [[NSNumber alloc] initWithUnsignedLongLong:([setting.LastIdentity unsignedLongLongValue]+1)];
	setting.LastIdentity = newIdentity;
	[newIdentity release];
	
	expense.Payment = setting.Payment;
	expense.Seller = setting.Seller;
	expense.SubCategory = setting.SubCategory;
	expense.Type = setting.Type;
	
	/*
	 The new book is associated with the add controller's managed object context.
	 This is good because it means that any edits that are made don't affect the application's main managed object context -- it's a way of keeping disjoint edits in a separate scratchpad -- but it does make it more difficult to get the new book registered with the fetched results controller.
	 First, you have to save the new book.  This means it will be added to the persistent store.  Then you can retrieve a corresponding managed object into the application delegate's context.  Normally you might do this using a fetch or using objectWithID: -- for example
	 
	 NSManagedObjectID *newBookID = [controller.book objectID];
	 NSManagedObject *newBook = [applicationContext objectWithID:newBookID];
	 
	 These techniques, though, won't update the fetch results controller, which only observes change notifications in its context.
	 You don't want to tell the fetch result controller to perform its fetch again because this is an expensive operation.
	 You can, though, update the main context using mergeChangesFromContextDidSaveNotification: which will emit change notifications that the fetch results controller will observe.
	 To do this:
	 1	Register as an observer of the add controller's change notifications
	 2	Perform the save
	 3	In the notification method (addControllerContextDidSave:), merge the changes
	 4	Unregister as an observer
	 */
	NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
	[dnc addObserver:self selector:@selector(addControllerContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:addingManagedObjectContext];

	if (![addingManagedObjectContext save:&error]) {
		// Update to handle the error appropriately.
		NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
		exit(-1);  // Fail
	}
	[dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:addingManagedObjectContext];
	[fetchedResults release];
	// Release the adding managed object context.
	self.addingManagedObjectContext = nil;

	//[self.appDelegate.expenseNavController pushViewController:editViewController animated:YES];*/
}

/**
 Notification from the add controller's context's save operation. This is used to update the fetched results controller's managed object context with the new book instead of performing a fetch (which would be a much more computationally expensive operation).
 */
- (void)addControllerContextDidSave:(NSNotification*)saveNotification {
	
	NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
	// Merging changes causes the fetched results controller to update its results
	[context mergeChangesFromContextDidSaveNotification:saveNotification];	
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
	if(editingStyle == UITableViewCellEditingStyleDelete) {
		NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
		[context deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]];
		
		NSError *error;
		if (![context save:&error]) {
			// Update to handle the error appropriately.
			NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
			exit(-1);  // Fail
		}
	}
}

#pragma mark -
#pragma mark Selection and moving

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
	
    // Create and push a detail view controller.
	ExpenseEditViewController *editViewController = [[ExpenseEditViewController alloc] initWithStyle:UITableViewStyleGrouped];
    Expense *selectedExpense = (Expense *)[[self fetchedResultsController] objectAtIndexPath:indexPath];
    // Pass the selected book to the new view controller.
    editViewController.expense = selectedExpense;
	[self.navigationController pushViewController:editViewController animated:YES];
	[editViewController release];
}


- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    // The table view should not be re-orderable.
    return NO;
}


// Invoked when the user touches Edit.
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
    // Updates the appearance of the Edit|Done button as necessary.
    [super setEditing:editing animated:animated];
    [self.listTableView setEditing:editing animated:YES];
    // Disable the add button while editing.
    if (editing) {
        self.navigationItem.rightBarButtonItem.enabled = NO;
    } else {
        self.navigationItem.rightBarButtonItem.enabled = YES;
    }
}

/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/

- (void)didReceiveMemoryWarning {
	// Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
	
	// Release any cached data, images, etc that aren't in use.
}


// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
	
	self.navigationItem.leftBarButtonItem = self.editButtonItem;
	
	UIBarButtonItem *addButton = [[UIBarButtonItem alloc] 
				 initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
				 target:self action:@selector(addExpense)];
	self.navigationItem.rightBarButtonItem = addButton;
	[addButton release];
	NSError *error;
	if (![[self fetchedResultsController] performFetch:&error]) {
		// Update to handle the error appropriately.
		NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
		exit(-1);  // Fail
	}
	
	
	[super viewDidLoad];
}

- (void)viewWillAppear {
	[self.listTableView reloadData];
}

- (void)viewDidUnload {
	// Release any retained subviews of the main view.
	// e.g. self.myOutlet = nil;
	self.fetchedResultsController = nil;
}

- (NSFetchedResultsController *)fetchedResultsController {
    
    if (fetchedResultsController != nil) {
        return fetchedResultsController;
    }
    
	// Create and configure a fetch request with the Book entity.
	NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
	NSEntityDescription *entity = [NSEntityDescription entityForName:@"Expense" 
											  inManagedObjectContext:managedObjectContext];
	[fetchRequest setEntity:entity];
	
	// Create the sort descriptors array.
	NSSortDescriptor *sortDateDescriptor = [[NSSortDescriptor alloc] initWithKey:@"Date" ascending:NO];	
	NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDateDescriptor, nil];
	
	[fetchRequest setSortDescriptors:sortDescriptors];
	
	// Create and initialize the fetch results controller.
	NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] 
															 initWithFetchRequest:fetchRequest 
															 managedObjectContext:managedObjectContext 
															 sectionNameKeyPath:nil 
															 cacheName:@"Root"];
	self.fetchedResultsController = aFetchedResultsController;
	fetchedResultsController.delegate = self;
	
	// Memory management.
	[aFetchedResultsController release];
	[fetchRequest release];
	[sortDateDescriptor release];
	[sortDescriptors release];
	
	return fetchedResultsController;
}    

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
	// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
	[self.listTableView beginUpdates];
}


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
	
	UITableView *tableView = self.listTableView;
	
	switch(type) {
			
		case NSFetchedResultsChangeInsert:
			[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
			break;
			
		case NSFetchedResultsChangeDelete:
			[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
			break;
			
		case NSFetchedResultsChangeUpdate:
			[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
			break;
			
		case NSFetchedResultsChangeMove:
			[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
			// Reloading the section inserts a new row and ensures that titles are updated appropriately.
			[tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
			break;
	}
}


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
	/*
	switch(type) {
			
		case NSFetchedResultsChangeInsert:
			[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
			break;
			
		case NSFetchedResultsChangeDelete:
			[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
			break;
	}*/
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
	// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
	[self.listTableView endUpdates];
}


- (void)dealloc {
	[fetchedResultsController release];
	[addingManagedObjectContext release];
	[managedObjectContext release];
	[listTableView release];

    [super dealloc];
}


@end
